/*-----------------------------------------------------------------------------------*/
/*                                                                                   */
/* Copyright (c) 2008 Nuvoton Technology Corporation                                 */
/* All rights reserved                                                               */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/
/* File Contents:                                                                    */
/*   ps2.c                                                                           */
/*                                                                                   */
/* This file contains:                                                               */
/*                                                                                   */
/* Project:                                                                          */
/*   1. The receive 2 bytes interrupt is not finished.                               */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/

#ifdef ECOS 
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
#include "drv_api.h"
#include "diag.h"
#include "wbio.h"
#else
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "wblib.h"
#endif

#include "NUC900_reg.h"
#include "NUC900_PS2.h"

/*-----------------------------------------*/
/* marco, type and constant definitions    */
/*-----------------------------------------*/

/*
	Define debug level.
*/
//#define PS2_DEBUG      /* normal info    */
//#define PS2_ISR_DEBUG  /* interrupt info */

#ifdef PS2_DEBUG
#ifdef ECOS
#define PS2DEBUG			diag_printf
#else
#define PS2DEBUG			sysprintf
#endif  /* ECOS */
#else
#define PS2DEBUG(...)
#endif  /* PS2_DEBUG */

#ifdef PS2_ISR_DEBUG
#ifdef ECOS
#define PS2IDEBUG			diag_printf
#else
#define PS2IDEBUG			sysprintf
#endif  /* ECOS */
#else
#define PS2IDEBUG(...)
#endif  /*  PS2_ISR_DEBUG*/

/*
	Defined for _ps2SendHostCommand. Detect the timeout case of transmit complete interrupt &
	receive interrupt. 
*/
#define PS2_WAIT_COUNT	    0x600000

/*
	Define the bits of PS2 host controller command register.
*/
#define PS2_EnCMD		    0x100
#define PS2_TRAP_SHIFT      0x200
#define PS2_RXEOFF          0x400
#define PS2_RXROFF          0x800

/*
	Define scan code for _ps2HandleKBLED()
*/
#define PS2_SCROLL_LOCK		0x7E
#define PS2_NUM_LOCK		0x77
#define PS2_CAPS_LOCK		0x58

#define PS2_NUM             2
#define PS2_DEV_NUM         3

#define ps2_out(dev, byte, addr)	outpw((dev)->uBaseAddr + addr, byte)
#define ps2_in(dev, addr)		    inpw((dev)->uBaseAddr + addr)

#define ps2RxBufReadNextOne(_ps2_uRxHead)	( ( (_ps2_uRxHead + 1) == PS2_RX_BUFFSIZE )? NULL: _ps2_uRxHead + 1 )
#define ps2RxBufWriteNextOne(_ps2_uRxTail)	( ( (_ps2_uRxTail + 1) == PS2_RX_BUFFSIZE )? NULL: _ps2_uRxTail + 1 )

typedef void (* _ps2FunPtr)();

typedef struct {		
	/*---------------------------*/
	/* Normal definitions        */
	/*---------------------------*/
	UINT32 uBaseAddr;                    /* base register address */
	volatile INT _ps2ErrNo;              /* error number          */
	volatile INT _ps2OpenFlag;           /* device open flag      */
	volatile INT _ps2RXOpMode;           /* RX operation mode     */
	
									     /* If set this flag, don't take care of key release */
	volatile INT _ps2RxRelease;          /* event. Default PS2_DISABLE_RX_RELEASE.           */
	
										 /* If set this flag, no ASCII or SCAN code will be       */
	volatile INT _ps2TrapShift;          /* reported for Shift key. Default PS2_ENABLE_TRAP_SHIFT */
	
	/*---------------------------*/
	/* _ps2SendHostCommand used  */
	/*---------------------------*/
	volatile INT _ps2CMDSent;            /* set in ps2ISR when transmit complete interrupt occurred */
	volatile INT _ps2CodeArrive;         /* it's set when receive interrupt occurred                */
		
	/*---------------------------*/
	/* PS2 RX buffer             */
	/*---------------------------*/
	volatile UINT32 uRxHead;                            
	volatile UINT32 uRxTail;                 
	volatile UINT32 uRecCnt;                  
	PS2_DATA_T _ps2RxBuf[PS2_RX_BUFFSIZE];    
	
	/*---------------------------*/
	/* Other definitions         */
	/*---------------------------*/	
	volatile INT _ps2DataReceive;         /* it's set when ASCII data received     */
	volatile BOOL bIsShiftKey;            /* it's true when Shift key received     */
	volatile BOOL bIsRelease;             /* it's true when receive released byte  */
	volatile BOOL bIsExtend;              /* it's true when receive extend byte    */
	volatile BOOL bIsMouseDevice;         /* it's true if device is PS2 mouse      */
	
	_ps2FunPtr _ps2DevHandler;            /* PS2 device handler table     */	
	_ps2FunPtr _ps2CallbackFun;	          /* PS2 device callback function */
	
}PS2_DEV_T;


/*-----------------------------------------*/
/* global file scope (static) variables    */
/*-----------------------------------------*/
static PS2_DEV_T ps2_dev[PS2_NUM];

static volatile UINT8 _ps2_ucScanCode, _ps2_ucAsciiCode;  

/*
	Defined for check the extended keyboard & numeric keypad.
*/
static UINT8 _ps2_NumericKeypad[10] = {0x69, 0x6B, 0x6C, 0x70, 0x72, 0x73, 0x74, 0x75, 0x7A, 0x7D};
static UINT8 _ps2_NumericAscii[10] = {0x31, 0x34, 0x37, 0x30, 0x32, 0x35, 0x36, 0x38, 0x33, 0x39};

/*
	Fix Shift key limitation of NUC900 PS2.
	
	Define table: [Scan Code] + [Key value] + [Shift + Key value]	
	For example: 
	'A' key, [1C] [61] [41] (Hex).
*/
static UINT8 _ps2_ASCIITable[47][3] = {
	{0x0E, 0x60, 0x7E},  /* ~ , ` */ 
	{0x16, 0x31, 0x21},  /* 1 , ! */
	{0x1E, 0x32, 0x40},  /* 2 , @ */
	{0x26, 0x33, 0x23},  /* 3 , # */
	{0x25, 0x34, 0x24},  /* 4 , $ */
	{0x2E, 0x35, 0x25},  /* 5 , % */
	{0x36, 0x36, 0x5E},  /* 6 , ^ */
	{0x3D, 0x37, 0x26},  /* 7 , & */
	{0x3E, 0x38, 0x2A},  /* 8 , * */
	{0x46, 0x39, 0x28},  /* 9 , ( */
	{0x45, 0x30, 0x29},  /* 0 , ) */
	{0x4E, 0x2D, 0x5F},  /* - , _ */
	{0x55, 0x3D, 0x2B},  /* = , + */
	{0x5D, 0x5C, 0x7C},  /* \ , | */
	
	{0x15, 0x71, 0x51},  /* q , Q */
	{0x1D, 0x77, 0x57},  /* w , W */
	{0x24, 0x65, 0x45},  /* e , E */
	{0x2D, 0x72, 0x52},  /* r , R */
	{0x2C, 0x74, 0x54},  /* t , T */
	{0x35, 0x79, 0x59},  /* y , Y */
	{0x3C, 0x75, 0x55},  /* u , U */
	{0x43, 0x69, 0x49},  /* i , I */
	{0x44, 0x6F, 0x4F},  /* o , O */
	{0x4D, 0x70, 0x50},  /* p , P */
	{0x54, 0x5B, 0x7B},  /* [ , { */
	{0x5B, 0x5D, 0x7D},  /* ] , } */
	
	{0x1C, 0x61, 0x41},  /* a , A */
	{0x1B, 0x73, 0x53},  /* s , S */
	{0x23, 0x64, 0x44},  /* d , D */
	{0x2B, 0x66, 0x46},  /* f , F */
	{0x34, 0x67, 0x47},  /* g , G */
	{0x33, 0x68, 0x48},  /* h , H */
	{0x3B, 0x6A, 0x4A},  /* j , J */
	{0x42, 0x6B, 0x4B},  /* k , K */
	{0x4B, 0x6C, 0x4C},  /* l , L */
	{0x4C, 0x3B, 0x3A},  /* ; , : */
	{0x52, 0x27, 0x22},  /* ' , " */
	
	{0x1A, 0x7A, 0x5A},  /* z , Z */
	{0x22, 0x78, 0x58},  /* x , X */
	{0x21, 0x63, 0x43},  /* c , C */
	{0x2A, 0x76, 0x56},  /* v , V */
	{0x32, 0x62, 0x42},  /* b , B */
	{0x31, 0x6E, 0x4E},  /* n , N */
	{0x3A, 0x6D, 0x4D},  /* m , M */
	{0x41, 0x2C, 0x3C},  /* , , < */
	{0x49, 0x2E, 0x3E},  /* . , > */
	{0x4A, 0x2F, 0x3F}   /* / , ? */
};	

/*
	Define for _ps2HandleKBLED()
*/
                                              /* 0: initail                                    */
                                              /* 1: receive specific scan code, 0x58 0x77 0x7E */
                                              /* 2: send first byte of write LED command       */
static volatile UINT8 _ps2_cKBLEDStatus = 0;  /* 3: send second byte of write LED command      */
static volatile UINT8  _ps2_ucCmd[2];  
static volatile UINT8 _ps2_ucLEDStatus = 0;   /* Maintain the LED status of LED command        */
static volatile BOOL bIsScrollLock = FALSE;   /* Scroll LED flag */
static volatile BOOL bIsNumLock = FALSE;      /* Num LED flag    */
static volatile BOOL bIsCapsLock = FALSE;     /* Caps LED flag   */

static volatile UINT32 ucPrevScanCode = 0;    /* keep the last scan code */

#ifdef ECOS
cyg_interrupt  ps2_int;
cyg_handle_t   ps2_int_handle;
#endif
  
/*-----------------------------------------*/
/* prototypes of static functions          */
/*-----------------------------------------*/
static VOID _ps2HandleKBEvent(PS2_DEV_T *dev);
static VOID _ps2HandleMouseEvent(PS2_DEV_T *dev);
static VOID _ps2HandleBarcodeEvent(PS2_DEV_T *dev);
static INT _ps2SendHostCommand(PS2_DEV_T *dev, PUINT8 pucBuf, UINT32 uLen);
static INT _ps2HandleKBLED(PS2_DEV_T *dev, UINT8 ucCode);
static INT _ps2HandleKBScanCode(PS2_DEV_T *dev);
static VOID _ps2HandleKeypadAscii(PS2_DEV_T *dev, UINT8 ucCode);
static INT _ps2PutQueue(PS2_DEV_T *dev);

static VOID _ps2FixShiftKeyBug (PS2_DEV_T *dev);  /* 2007.10.03 */  

static _ps2FunPtr ps2DevHandlerTable[PS2_DEV_NUM] = {
	_ps2HandleKBEvent, 
	_ps2HandleMouseEvent, 
	_ps2HandleBarcodeEvent
};

//-----------------------------------------------------------------------------------//
#ifdef ECOS
static cyg_uint32 ps2ISR(cyg_vector_t vector, cyg_addrword_t data)
#else
VOID ps2ISR(VOID)
#endif
{
	volatile UINT32 uRegGASR, uStatus; 
	PS2_DEV_T *dev; 
  
	uRegGASR = inpw(REG_AIC_GASR);
	
	/*-------------------------------*/
	/* PS2 0                         */
	/*-------------------------------*/
	if (uRegGASR & 0x10000000)
	{
		dev = (PS2_DEV_T *)((UINT32) &ps2_dev[0] | 0x80000000);
		
		uStatus = inpw(REG_PS2_STS0);
		switch (uStatus)
		{
			case PS2_STS_RX_IRQ:
				outpw(REG_PS2_STS0, PS2_STS_RX_IRQ);  /* write '1' to clear              */	
				(dev -> _ps2DevHandler)(dev);         /* execute specific device handler */ 
				break;
			
			case PS2_STS_RX_2BYTES:  // ..... not yet, press the same key continually case 
				outpw(REG_PS2_STS0, PS2_STS_RX_IRQ);  /* write '1' to clear */	
				PS2IDEBUG("PS2: port0 rec 2 bytes!");  
				break;
				
			case PS2_STS_TX_IRQ:
				outpw(REG_PS2_STS0, PS2_STS_TX_IRQ);  /* write '1' to clear */	
				dev -> _ps2CMDSent = TRUE;
				break;
				
			case PS2_STS_TX_ERR:
				PS2IDEBUG("PS2: WARNING! port0 TX_ERR Bit is set!\n");
				break;
				
			default:
				PS2IDEBUG("PS2: port0 unknow status[0x%x]\n", uStatus);
				break;			
		}
	}
	
	/*-------------------------------*/
	/* PS2 1                         */
	/*-------------------------------*/
	if (uRegGASR & 0x20000000)
	{
		dev = (PS2_DEV_T *)((UINT32) &ps2_dev[1] | 0x80000000);
		
		uStatus = inpw(REG_PS2_STS1);
		switch (uStatus)
		{
			case PS2_STS_RX_IRQ:
				outpw(REG_PS2_STS1, PS2_STS_RX_IRQ);  /* write '1' to clear              */
				(dev -> _ps2DevHandler)(dev);         /* execute specific device handler */ 
				break;
			
			case PS2_STS_RX_2BYTES:  // ..... not yet, press the same key continually case 
				outpw(REG_PS2_STS1, PS2_STS_RX_IRQ);  /* write '1' to clear */
				PS2IDEBUG("PS2: port1 rec 2 bytes!");  
				break;
				
			case PS2_STS_TX_IRQ:
				outpw(REG_PS2_STS1, PS2_STS_TX_IRQ);  /* write '1' to clear */	
				dev -> _ps2CMDSent = TRUE;
				break;
				
			case PS2_STS_TX_ERR:
				PS2IDEBUG("PS2: WARNING! port1 TX_ERR Bit is set!\n");
				break;
				
			default:
				PS2IDEBUG("PS2: port1 unknow status[0x%x]\n", uStatus);
				break;			
		}
	}
	
#ifdef ECOS
	cyg_drv_interrupt_acknowledge(vector);
	return CYG_ISR_HANDLED;
#endif	
}

static VOID _ps2HandleKBEvent(PS2_DEV_T *dev)
{
	INT nRet;
	volatile UINT32 uReg;
			
	dev -> _ps2CodeArrive = TRUE;
	
	/*---------------------------------------------------------------*/
	/* Identfy received shift key, release byte, extend byte.        */
	/*---------------------------------------------------------------*/	
	uReg = ps2_in(dev, PS2_SCANCODE);
	if (uReg & 0x100)                  /* extend byte */  
		dev -> bIsExtend = TRUE;
	else
		dev -> bIsExtend = FALSE;	
		
	if (uReg & 0x200)                  /* release byte */  
	{
		dev -> bIsRelease = TRUE;
		if(dev -> _ps2RxRelease == PS2_DISABLE_RX_RELEASE) 
			return;
	}
	else
		dev -> bIsRelease = FALSE;
		
	if (uReg & 0x400)                  /* shift key */  
		dev -> bIsShiftKey = TRUE;
	else
		dev -> bIsShiftKey = FALSE;	
		
	_ps2_ucScanCode = uReg & 0xFF;
	_ps2_ucAsciiCode = ps2_in(dev, PS2_ASCII) & 0xFF;
			
	nRet = _ps2HandleKBLED(dev, _ps2_ucScanCode);	
	if(nRet < 0)  /* scan code isn't a LED key */
		_ps2HandleKBScanCode(dev);	
}

static VOID _ps2HandleMouseEvent(PS2_DEV_T *dev)
{
	volatile UINT32 uReg;

	dev -> _ps2CodeArrive = TRUE;
	
	uReg = ps2_in(dev, PS2_SCANCODE);
	if(uReg & 0x100)                  /* extend byte */
		dev -> bIsExtend = TRUE;
	else
		dev -> bIsExtend = FALSE;	
		
	if(uReg & 0x200)                  /* release byte */
	{
		dev -> bIsRelease = TRUE;
		return;                       /* don't take care of the key release event */
	}
	else
		dev -> bIsRelease = FALSE;
		
	if(uReg & 0x400)                  /* shift key */
		dev -> bIsShiftKey = TRUE;
	else
		dev -> bIsShiftKey = FALSE;	
	
	_ps2_ucScanCode = uReg & 0xFF;
	_ps2_ucAsciiCode = ps2_in(dev, PS2_ASCII) & 0xFF;
	
	_ps2PutQueue(dev);
}

static VOID _ps2HandleBarcodeEvent(PS2_DEV_T *dev)
{
	volatile UINT32 uReg;
	
	uReg = ps2_in(dev, PS2_SCANCODE);
	if(uReg & 0x100)                  /* extend byte */
		dev -> bIsExtend = TRUE;
	else
		dev -> bIsExtend = FALSE;	
		
	if(uReg & 0x200)                  /* release byte */
	{
		dev -> bIsRelease = TRUE;
		return;                       /* don't take care of the key release event */
	}
	else
		dev -> bIsRelease = FALSE;
		
	if(uReg & 0x400)                  /* shift key */
		dev -> bIsShiftKey = TRUE;
	else
		dev -> bIsShiftKey = FALSE;	
	
	_ps2_ucScanCode = uReg & 0xFF;
	_ps2_ucAsciiCode = ps2_in(dev, PS2_ASCII) & 0xFF;
	
	_ps2PutQueue(dev);
}

static INT _ps2HandleKBLED(PS2_DEV_T *dev, UINT8 ucCode)
{
	if(_ps2_cKBLEDStatus == 0)
	{
		switch(ucCode)
		{
			case PS2_SCROLL_LOCK:              /* 0x7E */
				if(bIsScrollLock == FALSE)  
				{
					_ps2_ucLEDStatus |= 0x01;  /* turn on */
					bIsScrollLock = TRUE;
				}
				else                           
				{
					_ps2_ucLEDStatus &= ~0x01; /* turn off */
					bIsScrollLock = FALSE;
				}			
				_ps2_cKBLEDStatus = 1;		
				break;
			
			case PS2_NUM_LOCK:                     /* 0x77 */
				if(ucPrevScanCode == 0)            /* Pause key is E1 14 77 E1 */
				{
					if(bIsNumLock == FALSE)  
					{
						_ps2_ucLEDStatus |= 0x02;  /* turn on */
						bIsNumLock = TRUE;
					}
					else                    
					{
						_ps2_ucLEDStatus &= ~0x02; /* turn off */ 
						bIsNumLock = FALSE;	
					}			
					_ps2_cKBLEDStatus = 1;		
				}
				break;
				
			case PS2_CAPS_LOCK:                /* 0x58 */
				if(bIsCapsLock == FALSE) 
				{
					_ps2_ucLEDStatus |= 0x04;  /* turn on */
					bIsCapsLock = TRUE;
				}
				else 
				{
					_ps2_ucLEDStatus &= ~0x04; /* turn off */
					bIsCapsLock = FALSE;
				}				
				_ps2_cKBLEDStatus = 1;	
				break;
				
			default:
				return -1;           /* scan code is not a lock key */	
		}
	}
	
	if(_ps2_cKBLEDStatus == 1)         /* set true if receive lock key */
	{
		_ps2_ucCmd[0] = KBCMD_SETLED;  /* keyboard command, write LED  */
		_ps2_ucCmd[1] = _ps2_ucLEDStatus;
		
		if(dev -> _ps2TrapShift == PS2_ENABLE_TRAP_SHIFT)
			ps2_out(dev, (_ps2_ucCmd[0] | PS2_EnCMD | PS2_TRAP_SHIFT), PS2_CMD);
		else
			ps2_out(dev, (_ps2_ucCmd[0] | PS2_EnCMD), PS2_CMD);
			
		_ps2_cKBLEDStatus = 2;
		return 0;  
	}
	
	if(_ps2_cKBLEDStatus == 2)
	{
		if(_ps2_ucScanCode == KBSTS_ACK)
		{
			if(dev -> _ps2TrapShift == PS2_ENABLE_TRAP_SHIFT)
			ps2_out(dev, (_ps2_ucCmd[1] | PS2_EnCMD | PS2_TRAP_SHIFT), PS2_CMD);
		else
			ps2_out(dev, (_ps2_ucCmd[1] | PS2_EnCMD), PS2_CMD);
		
			_ps2_cKBLEDStatus = 3;
			return 0;  
		}		
	}
		
	if(_ps2_cKBLEDStatus == 3)
	{
		if(_ps2_ucScanCode == KBSTS_ACK)
		{
			_ps2_cKBLEDStatus = 0;
			return 0;  
		}	
	}	
	
	return -1;  /* can't receive ACK from keyboard */
}

static INT _ps2HandleKBScanCode(PS2_DEV_T *dev)
{
	/* First, if there is a ASCII code fill it into RX buffer. */
	if ( (_ps2_ucAsciiCode != 0x2E) || (_ps2_ucScanCode == 0x49) )  /* special case '.' */
	{
		/* 
			If Caps Lock enable, should check shift key status. 
		   Caps Lock enable & press shift key, capital change to lower case.
		   Caps Lock enable & not press shift key, lower case change to capital. 
		*/
		if (bIsCapsLock == TRUE) 
		{
			if (dev -> bIsShiftKey != TRUE)
			{
				if ( (_ps2_ucAsciiCode >= 0x61) && (_ps2_ucAsciiCode <= 0x7A) )  
					_ps2_ucAsciiCode -= 0x20;  /* lower case -> capital */	
			}	
			else  // press shift key, capital
			{
				if ( (_ps2_ucAsciiCode >= 0x41) && (_ps2_ucAsciiCode <= 0x5A) )  
					 _ps2_ucAsciiCode += 0x20;  /* capital -> lower case */
			}
		}
		else
		{
			/*
				Because NUC900 PS2's limitation, driver has to handle this case that user press 
				the Shift & other key at the same time. And if user press Shift & the specific key twice, 
				then press this specific key. Ths ASCII value of it will be wrong. 
				For example:
				Shift + 'A' = 0x41 (press twice)
				'A' = 0x41 <- wrong 
				
				(2007.10.03)
			*/
			_ps2FixShiftKeyBug(dev);				
		}
				
		goto putbuf;
	}
	
	/* Second, handle other scan code that have no ASCII code. */
	// Special prefix scancode.
	if( (_ps2_ucScanCode == 0xE1) /*|| (bIsExtend == TRUE)*/ )  
	{
		if(ucPrevScanCode == 0x200)  // receive 2th 0xE1 of "Pause" key
		{
			_ps2_ucScanCode = KB_E1_PAUSE;
			ucPrevScanCode = 0;
			goto putbuf;
		}	
		
		ucPrevScanCode = _ps2_ucScanCode;  // receive lst 0xE1 of "Pause" key
		return 0;	
	}
	
	// 0xFF is sent by a few keyboards, ignore it. 0x00 is error. 
	if( (_ps2_ucScanCode == 0x00) || (_ps2_ucScanCode == 0xFF) ) 
	{
		ucPrevScanCode = 0;
		return 0;
	}
	
	// Don't care these scancodes. 
	if( (_ps2_ucScanCode == KBSTS_ACK) || (_ps2_ucScanCode == KBSTS_RESET_OK) ||
	    (_ps2_ucScanCode == KBCMD_ECHO) ) 
	{
		ucPrevScanCode = 0;
		return 0;
	}
	
	// Press "Pause" key generates E1 14 77 E1, and nothing when released.
	if(ucPrevScanCode)
	{
		if(_ps2_ucScanCode == 0x14)
		{
			ucPrevScanCode = 0x100;
			return 0;
		}	
		else if( (ucPrevScanCode == 0x100) && (_ps2_ucScanCode == 0x77) )	
		{
			ucPrevScanCode = 0x200;
			return 0;
		}
	}
	
	/* Third, handle the numeric keypad including 0~9 keys. If number lock enable,
	   PS2 library have to fill the specific ASCII code of ucAsciiCode. Otherwise, 
	   just fill the scancode into RX buffer. */
	_ps2HandleKeypadAscii(dev, _ps2_ucScanCode);  	
	
putbuf:	
	
	/* Normal scancodes should fill into RX buffer directly. */
	_ps2PutQueue(dev);
	
	return 0;
}

static VOID _ps2FixShiftKeyBug (PS2_DEV_T *dev)
{
	INT i = 0;
	
	for (i = 0; i < 47; i++)
	{
		if (_ps2_ucScanCode == _ps2_ASCIITable[i][0])
			break;	
	}
	
	if (i == 47)  /* don't match  */
		return;
		
	if (dev -> bIsShiftKey == TRUE)
		_ps2_ucAsciiCode = _ps2_ASCIITable[i][2];
	else
		_ps2_ucAsciiCode = _ps2_ASCIITable[i][1];	
}

static VOID _ps2HandleKeypadAscii(PS2_DEV_T *dev, UINT8 ucCode)
{
	INT i;
	
	/* 
		Check the extended keyboard & numeric keypad of direction keys. 
		The different of direction keys is extended code, E0.
		For example, E0 74 & 74 mean "->".	
	*/
	if(dev -> bIsExtend == FALSE)
	{
		for(i = 0 ; i < 10 ; i++)
		{
			if(_ps2_NumericKeypad[i] == ucCode)
				break;	
		}
			
		if( (i < 10) && (bIsNumLock == TRUE) )
			_ps2_ucAsciiCode = _ps2_NumericAscii[i];	
	}
}

static INT _ps2PutQueue(PS2_DEV_T *dev)
{
	volatile UINT32 uBuf;
	
	uBuf = ps2RxBufWriteNextOne(dev -> uRxTail);
	if(uBuf == dev -> uRxHead)        /* RX buffer full */
	{
		PS2DEBUG("PS2: _ps2HandleBarcodeEvent, RX buffer full!\n");
		dev -> _ps2ErrNo = PS2_RXBUFFULL;
		return -1;
	}
	
	dev -> _ps2DataReceive = TRUE;	
	dev -> _ps2RxBuf[dev -> uRxTail].ucExtendByte = dev -> bIsExtend;
	dev -> _ps2RxBuf[dev -> uRxTail].ucScanCode = _ps2_ucScanCode;
	dev -> _ps2RxBuf[dev -> uRxTail].ucAsciiCode = _ps2_ucAsciiCode;
	dev -> uRxTail = ps2RxBufWriteNextOne(dev -> uRxTail);
	dev -> uRecCnt++;
	
	/* execute callback function */
	if(dev -> _ps2CallbackFun != NULL)
		(dev -> _ps2CallbackFun)();	
	
	return 0;
}

static INT _ps2SendHostCommand(PS2_DEV_T *dev, PUINT8 pucBuf, UINT32 uLen)
{
	UINT8 unAckCnt = 0;
	UINT32 uCnt = 0, i = 0, j = 0;
	PUINT8 pucCmd = pucBuf;
		
	/*---------------------------------------------------------------------------*/
	/* Send PS2 command to device byte by byte & receive ACK from device.        */
	/*---------------------------------------------------------------------------*/	
	while (uLen--)
	{
		dev -> _ps2CMDSent = FALSE;
		dev -> _ps2CodeArrive = FALSE;
		
		if (dev -> bIsMouseDevice == TRUE)
		{
			ps2_out(dev, (*pucCmd | PS2_EnCMD | PS2_RXEOFF | PS2_RXROFF), PS2_CMD);
		}
		else
		{
			if (dev -> _ps2TrapShift == PS2_ENABLE_TRAP_SHIFT)
				ps2_out(dev, (*pucCmd | PS2_EnCMD | PS2_TRAP_SHIFT), PS2_CMD);
			else
				ps2_out(dev, (*pucCmd | PS2_EnCMD), PS2_CMD);
		}		
			
		while (dev -> _ps2CMDSent == FALSE)
		{
			i++;
			if (i == PS2_WAIT_COUNT)
			{
				PS2DEBUG("PS2: _ps2SendHostCommand, send command time out!\n");
				dev -> _ps2ErrNo = PS2_SENDCOMMTIMEOUT;
				return -1;
			}	
		}
		
		/* Waiting for ACK from device, some keyboard commands should receive ACK twice. */
		if (dev -> bIsMouseDevice == TRUE)
		{
			if (*pucCmd == MOUSECMD_RESET)
				unAckCnt = 2;
			else
				unAckCnt = 1;
		}
		else
		{
			if (*pucCmd == KBCMD_RESET)
				unAckCnt = 2;
			else
				unAckCnt = 1;
		}
			
		for (j = unAckCnt; j > 0; j--)
		{
			i = 0;
			while (dev -> _ps2CodeArrive == FALSE)
			{
				i++;
				if (i == PS2_WAIT_COUNT)
				{
					PS2DEBUG("PS2: _ps2SendHostCommand, receive ACK time out!\n");
					dev -> _ps2ErrNo = PS2_RECACKTIMEOUT;
					return -1;
				}
			}
			dev -> _ps2CodeArrive = FALSE;
			
			/* check ACK */
			if (dev -> bIsMouseDevice == TRUE)
			{
				if ( (_ps2_ucScanCode != MOUSESTS_ACK) && (_ps2_ucScanCode != MOUSESTS_BATCOMPLT) )
				{
					dev -> _ps2ErrNo = PS2_RECACKERROR;
					return -1;					
				}
			}
			else
			{
				if ( (_ps2_ucScanCode != KBSTS_ACK) && (_ps2_ucScanCode != KBCMD_ECHO) &&
					(_ps2_ucScanCode != KBSTS_RESET_OK) )
				{	
					PS2DEBUG("PS2: ps2SendKBCommand, ACK value incorrect!\n");
					dev -> _ps2ErrNo = PS2_RECACKERROR;
					return -1;
				}
			}			
		}
		/* End of receive ACK */
				
		pucCmd++;
		uCnt++;			
	}
	
	return (uCnt);
}

/*-----------------------------------------------------------------------------------*/
/* Function:                                                                         */
/*   ps2Init                                                                         */
/*                                                                                   */
/* Parameters:                                                                       */
/*   None.                                                                           */
/*                                                                                   */                                                                           
/* Returns:                                                                          */
/*   0        Success.                                                                */
/*                                                                                   */
/* Side effects:                                                                     */
/*   None.                                                                           */
/*                                                                                   */
/* Description:                                                                      */
/*   Configure GPIO pins as PS2, PS2CLK & PS2DATA. Enable PS2 input clock.           */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/

INT ps2Init (VOID)
{
	outpw(REG_MFSEL, inpw(REG_MFSEL) | (0x03 << 20));  /* configure GPIO pins as PS2 */
	outpw(REG_CLKEN, inpw(REG_CLKEN) | (0x01 << 24));  /* enable PS2 clock           */
	return 0;	
}   

/*-----------------------------------------------------------------------------------*/
/* Function:                                                                         */
/*   ps2Open                                                                         */
/*                                                                                   */
/* Parameters:                                                                       */
/*   param         PS2 port number.                                                  */
/*   devid         PS2 device ID.                                                    */
/*                                                                                   */                                                                           
/* Returns:                                                                          */
/*   0             Success.                                                          */
/*   PS2_ENODEV    Interface number is incorrect.                                    */
/*   PS2_EIO       PS2 has opened before.                                            */
/*                                                                                   */
/* Side effects:                                                                     */
/*   None.                                                                           */
/*                                                                                   */
/* Description:                                                                      */
/*   Initialize PS2 structure and install ISR then enable interrupt.                 */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/

INT ps2Open (INT param, INT devid)
{
	PS2_DEV_T *dev;
	
	if ( (param != PS2_PORT0) && (param != PS2_PORT1) )
		return PS2_ENODEV;
		
	if ( (devid != PS2_KEYBOARD) && (devid != PS2_MOUSE) && (devid != PS2_BARCODE) )
		return PS2_ENODEV;
		
	dev = (PS2_DEV_T *)((UINT32) &ps2_dev[param] | 0x80000000);
	if (dev -> _ps2OpenFlag != 0)
		return PS2_EIO;
	
	/*---------------------------------------------------------------------------*/
	/* Initialize PS2 Struct                                                     */
	/*---------------------------------------------------------------------------*/
	memset(dev, 0, sizeof(PS2_DEV_T));	
	dev -> _ps2DevHandler = (_ps2FunPtr) ps2DevHandlerTable[devid];  /* Install PS2 device handler       */
	dev -> _ps2CallbackFun = NULL;									 /* Initialize PS2 callback function */
	dev -> _ps2OpenFlag = 1;
	dev -> _ps2RXOpMode = PS2_NON_BLOCK;
	dev -> _ps2RxRelease = PS2_DISABLE_RX_RELEASE;
	dev -> _ps2TrapShift = PS2_ENABLE_TRAP_SHIFT;
	dev -> uBaseAddr = (param) ? PS21_BA : PS20_BA;
	
	if (devid == PS2_MOUSE)
		dev -> bIsMouseDevice = TRUE;
	else
	{
		dev -> bIsMouseDevice = FALSE;	
		ps2_out(dev, PS2_TRAP_SHIFT, PS2_CMD);  /* Set TRAP_SHIFT bit */
	}	
		
	_ps2_cKBLEDStatus = 0;
	
	/*---------------------------------------------------------------------------*/
	/* Install PS2 Interrupt Service Routine                                     */
	/*---------------------------------------------------------------------------*/
#ifdef ECOS	
	cyg_drv_interrupt_create(CYGNUM_HAL_INTERRUPT_PS2_GROUP, 99, 0, ps2ISR, NULL, &ps2_int_handle, &ps2_int);
    cyg_drv_interrupt_attach(ps2_int_handle);
    cyg_drv_interrupt_unmask(CYGNUM_HAL_INTERRUPT_PS2_GROUP);
#else	
	sysInstallISR(IRQ_LEVEL_1, IRQ_PS2_GROUP, (PVOID)ps2ISR);
   	sysEnableInterrupt(IRQ_PS2_GROUP);  
	sysSetLocalInterrupt(ENABLE_IRQ);                       /* enable CPSR I bit */
#endif	
	
	if (param == PS2_PORT0)
		outpw(REG_AIC_GEN, inpw(REG_AIC_GEN) | (1 << 28));  /* important enable PS2 0 interrupt group */
	else
		outpw(REG_AIC_GEN, inpw(REG_AIC_GEN) | (1 << 29));  /* important enable PS2 1 interrupt group */
	
	return 0;	
}

/*-----------------------------------------------------------------------------------*/
/* Function:                                                                         */
/*   ps2Read                                                                         */
/*                                                                                   */
/* Parameters:                                                                       */
/*   nNum          PS2 port number.                                                  */
/*   pucBuf        Receive buffer pointer.                                           */
/*   uLen          Receive buffer length.                                            */
/*                                                                                   */
/* Returns:                                                                          */
/*   > 0           Return read length on success.                                    */
/*   0             There is no data in PS2 RX buffer or uLen is zero.                */
/*   PS2_ENODEV    Incorrect interface number.                                       */
/*   PS2_EIO       No activated or write failed.                                     */
/*                                                                                   */
/* Side effects:                                                                     */
/*   None.                                                                           */
/*                                                                                   */
/* Description:                                                                      */
/*   Get scan code, ASCII code and extend flag from PS2 Rx buffer.                   */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/

INT ps2Read(INT nNum, PUINT8 pucBuf, UINT32 uLen) 
{
	INT i, cnt = 0;	
	PS2_DEV_T *dev;
	PS2_DATA_T *rb;
	
	if ( (nNum != PS2_PORT0) && (nNum != PS2_PORT1) )
		return PS2_ENODEV;
	
	dev = (PS2_DEV_T *)((UINT32) &ps2_dev[nNum] | 0x80000000);
	if (dev -> _ps2OpenFlag == 0)
		return PS2_EIO;
		
	if (dev -> _ps2ErrNo != 0)
		return PS2_EIO;	
		
	if (uLen == 0)
		return 0;	
	
	rb = (PS2_DATA_T *) pucBuf;
	
	if (dev -> _ps2RXOpMode == PS2_NON_BLOCK)
	{
		if (dev -> uRecCnt == 0)
			return 0;
			
		if (uLen > dev -> uRecCnt)
			uLen = dev -> uRecCnt;	
		
		for (i = dev -> uRecCnt; i > 0; i--)
		{
			rb->ucExtendByte = dev -> _ps2RxBuf[dev -> uRxHead].ucExtendByte;
			rb->ucScanCode = dev -> _ps2RxBuf[dev -> uRxHead].ucScanCode;
			rb->ucAsciiCode = dev -> _ps2RxBuf[dev -> uRxHead].ucAsciiCode;
			dev -> uRxHead = ps2RxBufReadNextOne(dev -> uRxHead);
			
			rb++;
			dev -> uRecCnt--;	
			cnt++;
		}
	}
	else if (dev -> _ps2RXOpMode == PS2_BLOCK)
	{
		/*--------------------------------------------------------------------------------*/
		/* Initialize Rx buffer & _ps2DataReceive flag in order to get the 1st input key. */
		/*--------------------------------------------------------------------------------*/
		dev -> uRxHead = NULL;
		dev -> uRxTail = NULL;
		dev -> uRecCnt = 0;
		dev -> _ps2DataReceive = FALSE;
		for (i = uLen; i > 0; i--)
		{
			while( !(dev -> _ps2DataReceive) );			
			dev -> _ps2DataReceive = FALSE;
			
			rb->ucExtendByte = dev -> _ps2RxBuf[dev -> uRxHead].ucExtendByte;
			rb->ucScanCode = dev -> _ps2RxBuf[dev -> uRxHead].ucScanCode;
			rb->ucAsciiCode = dev -> _ps2RxBuf[dev -> uRxHead].ucAsciiCode;
			dev -> uRxHead = ps2RxBufReadNextOne(dev -> uRxHead);
			
			rb++;
			dev -> uRecCnt--;	
			cnt++;
		}
	}
	
	return cnt;	
}		

/*-----------------------------------------------------------------------------------*/
/* Function:                                                                         */
/*   ps2Write                                                                        */
/*                                                                                   */
/* Parameters:                                                                       */
/*   nNum          PS2 port number.                                                  */
/*   pucBuf        Transmit buffer pointer.                                          */
/*   uLen          Transmit buffer length.                                           */
/*                                                                                   */                                                                           
/* Returns:                                                                          */
/*   > 0           Return write length on success.                                   */
/*   0             Write length is zero.                                             */
/*   PS2_ENODEV    Incorrect interface number.                                       */
/*   PS2_EIO       No activated or write failed.                                     */
/*                                                                                   */
/* Side effects:                                                                     */
/*   None.                                                                           */
/*                                                                                   */
/* Description:                                                                      */
/*   Send host command to PS2 device.                                                */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/

int ps2Write(INT nNum, PUINT8 pucBuf, UINT32 uLen)   
{
	INT nSendLen;
	PS2_DEV_T *dev;
	
	if ( (nNum != PS2_PORT0) && (nNum != PS2_PORT1) )
		return PS2_ENODEV;
	
	dev = (PS2_DEV_T *)((UINT32) &ps2_dev[nNum] | 0x80000000);
	if (dev -> _ps2OpenFlag == 0)
		return PS2_EIO;
		
#if 0  // Make sure that the nxet command can be send.    2007.12.26 mark, PT23 HHWu		
	if (dev -> _ps2ErrNo != 0)
		return PS2_EIO;	
#endif		
	
	if (uLen == 0)
		return 0;
	
	nSendLen = _ps2SendHostCommand(dev, pucBuf, uLen);
	if(nSendLen < 0)
		return PS2_EIO;		

	return (nSendLen);
}

/*-----------------------------------------------------------------------------------*/
/* Function:                                                                         */
/*   ps2Ioctl                                                                        */
/*                                                                                   */
/* Parameters:                                                                       */
/*   nNum			PS2 port number.                                                 */
/*	 uCmd			Ioctl command.                                                   */
/*	 uArg0			Arguments of the command.                                        */
/*   uArg1			Arguments of the command.                                        */
/*                                                                                   */                                                                           
/* Returns:                                                                          */
/*   0				Success.                                                         */
/*	 PS2_ENOTTY		Command not support, or parameter incorrect.                     */
/*	 PS2_ENODEV		Interface number incorrect.                                      */
/*	 PS2_EIO		No activated or write failed.                                    */
/*                                                                                   */
/* Side effects:                                                                     */
/*   None.                                                                           */
/*                                                                                   */
/* Description:                                                                      */
/*   Support some PS2 driver commands for application.                               */
/*                                                                                   */
/*   uCmd:                                                                           */
/*	 PS2_IOC_SET_TRAP_SHIFT			uArg0: ENABLE_TRAP_SHIFT/DISABLE_TRAP_SHIFT      */
/*	 PS2_IOC_SET_RX_RELEASE			uArg0: ENABLE_RX_RELEASE/DISABLE_RX_RELEASE      */
/*	 PS2_IOC_SET_CALLBACK_FUN		uArg0: Callback function pointer                 */
/* 	 PS2_IOC_SET_RX_MODE			uArg0: PS2_BLOCK/PS2_NON_BLOCK                   */
/*	 PS2_IOC_DISABLE_CALLBACK_FUN	No argument.                                     */
/*	 PS2_IOC_FLUSH_RX_BUFFER		No argument.                                     */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/

INT ps2Ioctl(INT nNum, UINT32 uCmd, UINT32 uArg0, UINT32 uArg1)
{
	PS2_DEV_T *dev;
	
	if ( (nNum != PS2_PORT0) && (nNum != PS2_PORT1) )
		return PS2_ENODEV;
	
	dev = (PS2_DEV_T *)((UINT32) &ps2_dev[nNum] | 0x80000000);
	if (dev -> _ps2OpenFlag == 0)
		return PS2_EIO;
	
	switch(uCmd)
	{
		case PS2_IOC_SET_RX_MODE:
			if( (uArg0 != PS2_BLOCK) && (uArg0 != PS2_NON_BLOCK) )
				return PS2_ENOTTY;
		
			dev -> _ps2RXOpMode = uArg0;
			break;
			
		case PS2_IOC_FLUSH_RX_BUFFER:
			dev -> uRxHead = 0;
			dev -> uRxTail = 0;
			dev -> uRecCnt = 0;
			break;		
			
		case PS2_IOC_SET_CALLBACK_FUN:
			if(uArg0 != NULL)
				dev -> _ps2CallbackFun = (_ps2FunPtr) uArg0;	
			
			break;	
			
		case PS2_IOC_DISABLE_CALLBACK_FUN:
			dev -> _ps2CallbackFun = NULL;
			break;	
			
		case PS2_IOC_SET_TRAP_SHIFT:
			if (uArg0 == PS2_ENABLE_TRAP_SHIFT)
			{
				dev -> _ps2TrapShift = PS2_ENABLE_TRAP_SHIFT;
				ps2_out(dev, PS2_TRAP_SHIFT, PS2_CMD);
								
				PS2DEBUG("PS2: Enable trap shift\n");
			}	
			else if (uArg0 == PS2_DISABLE_TRAP_SHIFT)
			{
				dev -> _ps2TrapShift = PS2_DISABLE_TRAP_SHIFT;
				ps2_out(dev, 0, PS2_CMD);
				
				PS2DEBUG("PS2: Disable trap shift\n");
			}
			else
				return PS2_ENOTTY;
			
			break;	
			
		case PS2_IOC_SET_RX_RELEASE:
			if (uArg0 == PS2_ENABLE_RX_RELEASE)
			{
				dev -> _ps2RxRelease = PS2_ENABLE_RX_RELEASE;
					
				PS2DEBUG("PS2: Enable RX release\n");
			}
			else if (uArg0 == PS2_DISABLE_RX_RELEASE)
			{
				dev -> _ps2RxRelease = PS2_DISABLE_RX_RELEASE;
					
				PS2DEBUG("PS2: Disable RX release\n");				
			}	
			else
				return PS2_ENOTTY;
			
			break;
			
		case PS2_IOC_GET_ERR_STATUS:
			*(PUINT32)uArg0 = dev -> _ps2ErrNo;			
			break;		
		
		default:
			PS2DEBUG("PS2: Unknow ioctl command!\n");
			return PS2_ENOTTY;	
	}
	
	return 0;	
}

/*-----------------------------------------------------------------------------------*/
/* Function:                                                                         */
/*   ps2Release                                                                      */
/*                                                                                   */
/* Parameters:                                                                       */
/*   fd			    PS2 port number.                                                 */
/*                                                                                   */                                                                           
/* Returns:                                                                          */
/*   0				Success.                                                         */
/*	 PS2_ENODEV		Incorrect interface number.                                      */
/*	 PS2_EIO		No activated or write failed.                                    */
/*                                                                                   */
/* Side effects:                                                                     */
/*   None.                                                                           */
/*                                                                                   */
/* Description:                                                                      */
/*   Disable the specific PS2 port interrupt. And initialize some parameters.        */
/*                                                                                   */
/*-----------------------------------------------------------------------------------*/

INT ps2Release (INT fd)
{
	PS2_DEV_T *dev;
	
	if ( (fd != PS2_PORT0) && (fd != PS2_PORT1) )
		return PS2_ENODEV;
		
	dev = (PS2_DEV_T *)((UINT32) &ps2_dev[fd] | 0x80000000);
	if (dev -> _ps2OpenFlag  == 0)
		return PS2_EIO;
		
	if (fd == PS2_PORT0)
		outpw(REG_AIC_GEN, inpw(REG_AIC_GEN) & ~(1 << 28));  /* disable PS2 0 interrupt group */
	else
		outpw(REG_AIC_GEN, inpw(REG_AIC_GEN) & ~(1 << 29));  /* disable PS2 1 interrupt group */
	
	dev -> _ps2OpenFlag = 0;
	dev -> _ps2DevHandler = NULL;
	dev -> _ps2CallbackFun = NULL;
	dev -> bIsMouseDevice = FALSE;			
	
	return 0;
}
